///////////////////////////////////////////////////////////////////////////////
//
/// Copyright (c) 2022- ThunderSoft
/// All Rights Reserved by Thunder Software Technology Co., Ltd and its affiliates.
/// You may not use, copy, distribute, modify, transmit in any form this file
/// except in compliance with ThunderSoft in writing by applicable law.
//
///////////////////////////////////////////////////////////////////////////////
#ifndef DRA_COM_SERIALIZECOMMON_H_
#define DRA_COM_SERIALIZECOMMON_H_


#include <tuple>
#include <type_traits>
#include <memory>
#include <array>
#include <vector>
#include <map>
#include <cstring>
// #include "dra/core/typedefine.h"
#include "serialize/typedefine.h"
// #include "string.h"
#include "serialize/ProtocolDef.h"

namespace dra{
namespace com{
// namespace someip{

// using namespace deviceware::base;  // NOLINT

const INT32 HEADER_LENGTH_ADDR_NUM = 4;
// serial/deserial tpye define
#define FOURCC(ch0, ch1, ch2, ch3) ((ch0 << 24) + (ch1 << 16) + (ch2 << 8) + (ch3 << 0))
#define SERIAL_TYPE_BOOL    (FOURCC('B', 'O', 'O', 'L'))
#define SERIAL_TYPE_INT8    (FOURCC('_', '_', 'I', '8'))
#define SERIAL_TYPE_INT16   (FOURCC('_', 'I', '1', '6'))
#define SERIAL_TYPE_INT32   (FOURCC('_', 'I', '3', '2'))
#define SERIAL_TYPE_INT64   (FOURCC('_', 'I', '6', '4'))
#define SERIAL_TYPE_UINT8   (FOURCC('_', 'U', 'I', '8'))
#define SERIAL_TYPE_UINT16  (FOURCC('U', 'I', '1', '6'))
#define SERIAL_TYPE_UINT32  (FOURCC('U', 'I', '3', '2'))
#define SERIAL_TYPE_UINT64  (FOURCC('U', 'I', '6', '4'))
#define SERIAL_TYPE_FLOAT32 (FOURCC('_', 'F', '3', '2'))
#define SERIAL_TYPE_FLOAT64 (FOURCC('_', 'F', '6', '4'))
#define SERIAL_TYPE_STR     (FOURCC('_', 'S', 'T', 'R'))


struct bigLittleSwap {
    inline static BOOLEAN isLittleend() {
        union {
            INT32 i;
            CHAR c;
        }un;
        un.i = 1;

        if (un.c == 1)
            return true;

        return false;
    }

    inline static INT16 swapInt16(INT16 x) {
        return (((((x) >> 8) & 0xff) | (((x) & 0xff) << 8)));
    }

    inline static INT32 swapInt32(INT32 x) {
        return ((((x) & 0xff000000u) >> 24) | (((x) & 0x00ff0000u) >> 8) \
                | (((x) & 0x0000ff00u) << 8) | (((x) & 0x000000ffu) << 24));
    }

    inline static INT64 swapInt64(INT64 x) {
        return   ((((x) & 0xff00000000000000ull) >> 56) \
                        | (((x) & 0x00ff000000000000ull) >> 40) \
                        | (((x) & 0x0000ff0000000000ull) >> 24) \
                        | (((x) & 0x000000ff00000000ull) >> 8)  \
                        | (((x) & 0x00000000ff000000ull) << 8)  \
                        | (((x) & 0x0000000000ff0000ull) << 24) \
                        | (((x) & 0x000000000000ff00ull) << 40) \
                        | (((x) & 0x00000000000000ffull) << 56));
    }

    inline static FLOAT32 swapFloat32(FLOAT32 fValue) {
        UINT8 swap1[4], swap2[4];
        FLOAT32 fResult;

        memcpy(swap1, &fValue, sizeof(FLOAT32));

        for (auto i = 0; i < 4; ++i) {
            swap2[i] = swap1[3-i];
        }

        memcpy(&fResult, swap2, sizeof(FLOAT32));

        return fResult;
    }

    inline static FLOAT64 swapFloat64(FLOAT64 fValue) {
        UINT8 swap1[8], swap2[8];
        FLOAT64 fResult;

        memcpy(swap1, &fValue, sizeof(FLOAT64));

        for (auto i = 0; i < 8; ++i) {
            swap2[i] = swap1[7-i];
        }

        memcpy(&fResult, swap2, sizeof(FLOAT64));

        return fResult;
    }
};

// Props --------------------------------------------------------------------------------------------------------------
using PositiveInteger = UINT32;
using Boolean = BOOLEAN;
enum class ByteOrderEnum : UINT32 {
    mostSignificantByteFirst = 0,
    mostSignificantByteLast  = 1,
    opaque                   = 2
};
enum class SOMEIPTransformerSessionHandlingEnum : UINT32 {
    reserve = 0
};
enum class BaseTypeEncodingString : UINT32 {
    UTF_8       = 0,
    UTF_16BE    = 1,
    UTF_16LE    = 2
};
typedef struct {
    PositiveInteger                         alignment;
    ByteOrderEnum                           byteOrder;
    Boolean                                 implementsLegacyStringSerialization;
    Boolean                                 isDynamicLengthFieldSize;
    SOMEIPTransformerSessionHandlingEnum    sessionHandling;
    PositiveInteger                         sizeOfArrayLengthField;
    PositiveInteger                         sizeOfStringLengthField;
    PositiveInteger                         sizeOfStructLengthField;
    PositiveInteger                         sizeOfUnionLengthField;
    PositiveInteger                         sizeOfUnionTypeSelectorField;
    BaseTypeEncodingString                  stringEncoding;
} ApSomeipTransformationProps;
const ApSomeipTransformationProps transProps {
    32,
    ByteOrderEnum::mostSignificantByteFirst,
    false,
    false,
    SOMEIPTransformerSessionHandlingEnum::reserve,
    4,
    4,
    4,
    4,
    4,
    BaseTypeEncodingString::UTF_8
};
// end Props ----------------------------------------------------------------------------------------------------------

template <typename T, typename Tag = void>
struct IsStruct {
    static const bool value = false;
};

template <typename T>
struct IsStruct<T, typename T::IsStructTag> {
    static const bool value = true;
};

template <typename T, typename Tag = void>
struct IsUnion {
    static const bool value = false;
};

template <typename T>
struct IsUnion<T, typename T::IsUnionTag> {
    static const bool value = true;
};

// begin GetLength ----------------------------------------------------------------------------------------------------
// generalization
template<typename T>
typename std::enable_if<!std::is_enum<T>::value && !IsUnion<T>::value && !IsStruct<T>::value, uint32_t>::type
GetLength(const T& data) {
    return data.GetLength(SERIALIZE_TYPE::SOMEIP);
}

template<typename T>
typename std::enable_if<!std::is_enum<T>::value && IsStruct<T>::value, uint32_t>::type
GetLength(const T& data) {
    return data.GetLength(SERIALIZE_TYPE::SOMEIP) + transProps.sizeOfStructLengthField;
}

template<typename T>
typename std::enable_if<!std::is_enum<T>::value && IsUnion<T>::value, uint32_t>::type
GetLength(const T& data) {
    return data.GetLength(SERIALIZE_TYPE::SOMEIP) + transProps.sizeOfUnionLengthField + 4;   // 4 is length of union type
}

// enum
template<typename T>
typename std::enable_if<std::is_enum<T>::value, uint32_t>::type
GetLength(const T& data) {
    return sizeof(static_cast<std::underlying_type_t<T>>(data));
}

// BOOLEAN
template<>
inline uint32_t GetLength<BOOLEAN>(const BOOLEAN& data) {
    return sizeof(data);
}

// char
template<>
inline uint32_t GetLength<char>(const char& data) {
    return sizeof(data);
}

// UINT8
template<>
inline uint32_t GetLength<UINT8>(const UINT8& data) {
    return sizeof(data);
}

// UINT16
template<>
inline uint32_t GetLength<UINT16>(const UINT16& data) {
    return sizeof(data);
}

// UINT32
template<>
inline uint32_t GetLength<UINT32>(const UINT32& data) {
    return sizeof(data);
}

// UINT64
template<>
inline uint32_t GetLength<UINT64>(const UINT64& data) {
    return sizeof(data);
}

#if !defined(__aarch64_oe__) && !defined(__aarch32_oe__) && !defined(__ANDROID__)
template<>
inline uint32_t GetLength<uint64_t>(const uint64_t& data) {
    return sizeof(data);
}
#endif

// INT8
template<>
inline uint32_t GetLength<INT8>(const INT8& data) {
    return sizeof(data);
}

// INT16
template<>
inline uint32_t GetLength<INT16>(const INT16& data) {
    return sizeof(data);
}

// INT32
template<>
inline uint32_t GetLength<INT32>(const INT32& data) {
    return sizeof(data);
}

// INT64
template<>
inline uint32_t GetLength<INT64>(const INT64& data) {
    return sizeof(data);
}

#if !defined(__aarch64_oe__) && !defined(__aarch32_oe__) && !defined(__ANDROID__)
template<>
inline uint32_t GetLength<int64_t>(const int64_t& data) {
    return sizeof(data);
}
#endif

// FLOAT32
template<>
inline uint32_t GetLength<FLOAT32>(const FLOAT32& data) {
    return sizeof(data);
}

// FLOAT64
template<>
inline uint32_t GetLength<FLOAT64>(const FLOAT64& data) {
    return sizeof(data);
}

// STRING
template<>
inline uint32_t GetLength<STRING>(const STRING& data) {
    if (transProps.stringEncoding == BaseTypeEncodingString::UTF_8) {
        return data.size() + 4 + transProps.sizeOfStringLengthField;
    } else {
        return data.size()*2 + 4 + transProps.sizeOfStringLengthField;
    }
}

template<>
inline uint32_t GetLength<fd_t>(const fd_t& data) {
    return sizeof(data.value);
}

// stl declare
template<typename First, typename Second>
inline uint32_t GetLength(const std::pair<First, Second>& data);

template<typename First, typename Second>
inline uint32_t GetLength(const std::map<First, Second>& data);

template<typename First, typename Second>
inline uint32_t GetLength(const std::unordered_map<First, Second>& data);

template<typename... Args>
inline uint32_t GetLength(const std::tuple<Args...>& data);

template<typename T, std::size_t N>
typename std::enable_if<!std::is_trivially_copyable<T>::value, uint32_t>::type
GetLength(const std::array<T, N>& data);

template<typename T, std::size_t N>
typename std::enable_if<std::is_trivially_copyable<T>::value, uint32_t>::type
GetLength(const std::array<T, N>& data);

template<typename T>
typename std::enable_if<!std::is_trivially_copyable<T>::value, uint32_t>::type
GetLength(const std::vector<T>& data);

template<typename T>
typename std::enable_if<std::is_trivially_copyable<T>::value, uint32_t>::type
GetLength(const std::vector<T>& data);

// Begin Only For ETS Test---------------------------------------------------------------------------------------------
template<UINT32 L = 0, BaseTypeEncodingString E = BaseTypeEncodingString::UTF_8>
struct STRINGW {
    STRINGW() = default;
    explicit STRINGW(STRING&& value) {
        str = value;
    }
    explicit STRINGW(size_t len) {
        str = STRING(len, '\0');
    }
    STRING str;
};

template <typename T, std::size_t N, UINT32 L = 0>
struct ARRAYW {
    ARRAYW() = default;
    explicit ARRAYW(std::array<T, N>&& value) {
        arr = value;
    }
    std::array<T, N> arr;
};

template <typename T, UINT32 L = 0>
struct VECTORW {
    VECTORW() = default;
    explicit VECTORW(std::vector<T>&& value) {
        vec = value;
    }
    std::vector<T> vec;
};
// End   Only For ETS Test---------------------------------------------------------------------------------------------

// std::vector
template<typename T>
typename std::enable_if<!std::is_trivially_copyable<T>::value, uint32_t>::type
GetLength(const std::vector<T>& data) {
    uint32_t len = 0;
    for (const auto& item : data) {
        len += GetLength(item);
    }
    return len + transProps.sizeOfArrayLengthField;
}

template<typename T>
typename std::enable_if<std::is_trivially_copyable<T>::value, uint32_t>::type
GetLength(const std::vector<T>& data) {
    return data.size() * sizeof(T) + transProps.sizeOfArrayLengthField;
}

// std::array
template<typename T, std::size_t N>
typename std::enable_if<!std::is_trivially_copyable<T>::value, uint32_t>::type
GetLength(const std::array<T, N>& data) {
    uint32_t len = 0;
    for (const auto& item : data) {
        len += GetLength(item);
    }
    return len;
}

template<typename T, std::size_t N>
typename std::enable_if<std::is_trivially_copyable<T>::value, uint32_t>::type
GetLength([[maybe_unused]] const std::array<T, N>& data) {
    return N * sizeof(T);
}

// Begin Only For ETS Test---------------------------------------------------------------------------------------------
// STRINGW
template<UINT32 L, BaseTypeEncodingString E>
inline uint32_t GetLength(const STRINGW<L, E>& data) {
    if (transProps.stringEncoding == BaseTypeEncodingString::UTF_8) {
        return data.str.size() + 4 + transProps.sizeOfStringLengthField;
    } else {
        return data.str.size()*2 + 4 + transProps.sizeOfStringLengthField;
    }
}

// ARRAYW
template<typename T, std::size_t N, UINT32 L>
typename std::enable_if<!std::is_trivially_copyable<T>::value, uint32_t>::type
GetLength(const ARRAYW<T, N, L>& data) {
    uint32_t len = 0;
    for (const auto& item : data.arr) {
        len += GetLength(item);
    }
    return len;
}

template<typename T, std::size_t N, UINT32 L>
typename std::enable_if<std::is_trivially_copyable<T>::value, uint32_t>::type
GetLength([[maybe_unused]] const ARRAYW<T, N, L>& data) {
    return N * sizeof(T);
}

// VECTORW
template<typename T, UINT32 L>
typename std::enable_if<!std::is_trivially_copyable<T>::value, uint32_t>::type
GetLength(const VECTORW<T, L>& data) {
    uint32_t len = 0;
    for (const auto& item : data.vec) {
        len += GetLength(item);
    }
    return len + L;
}

template<typename T, UINT32 L>
typename std::enable_if<std::is_trivially_copyable<T>::value, uint32_t>::type
GetLength(const VECTORW<T, L>& data) {
    return data.vec.size() * sizeof(T) + L;
}
// End   Only For ETS Test---------------------------------------------------------------------------------------------

// std::pair
template<typename First, typename Second>
inline uint32_t GetLength(const std::pair<First, Second>& data) {
    return GetLength(data.first) + GetLength(data.second);
}

// std::map
template<typename First, typename Second>
inline uint32_t GetLength(const std::map<First, Second>& data) {
    uint32_t len = 0;
    for (const auto& item : data) {
        len += GetLength(item);
    }
    return len;
}

// std::unordered_map
template<typename First, typename Second>
inline uint32_t GetLength(const std::unordered_map<First, Second>& data) {
    uint32_t len = 0;
    for (const auto& item : data) {
        len += GetLength(item);
    }
    return len;
}
template <typename... Args>
struct getlength_wrapper;

// std::tuple
template<typename... Args>
inline uint32_t GetLength(const std::tuple<Args...>& data) {
    return getlength_wrapper<std::tuple<Args...>>::GetTupleLength(data);
}

template <typename... Args>
struct getlength_wrapper {
    inline static uint32_t GetMultiParamLength(Args... args) {
        return IndexArgs(args...);
    }

private:
    template<typename Head, typename... Tail>
    inline static uint32_t IndexArgs(Head head, Tail... tails) {
        return IndexArgs(head) + IndexArgs(tails...);
    }

    template<typename Head>
    inline static uint32_t IndexArgs(Head head) {
        uint32_t len = GetLength(head);
        return len;
    }

    inline static uint32_t IndexArgs(){
        return 0;
    }
};

template <typename... Args>
struct getlength_wrapper<std::tuple<Args...>> {
    inline static uint32_t GetTupleLength(const std::tuple<Args...>& t) {
        using indices_for = std::make_index_sequence<std::tuple_size<std::tuple<Args...>>::value>;
        return _GetTupleLength(t, indices_for{});
    }

private:
    template <std::size_t... Is>
    inline static uint32_t _GetTupleLength(const std::tuple<Args...>& t, const std::index_sequence<Is...>&) {
        return getlength_wrapper<Args...>::GetMultiParamLength(std::get<Is>(t)...);
    }
};


template<typename... Args>
inline uint32_t GetLength(Args... args) {
    return getlength_wrapper<Args...>::GetMultiParamLength(args...);
}

template<typename... Args>
UINT32 GetLength([[maybe_unused]] SERIALIZE_TYPE type, Args... args) {
    return GetLength(args...);
}
// end GetLength ------------------------------------------------------------------------------------------------------


// };  // namespace someip
};  // namespace com
};  // namespace dra
#endif  // DRA_COM_SERIALIZECOMMON_H_
